Au tout début on a 870 lignes et 381 IDs.
Après avoir exclu 4 lignes à cause de missing ID, on descend à 866 questionnaires et 380 IDs (l’ID “valeur manquante” n’est plus comptabilisé, c’est pour ça qu’on “perd” un ID).
Sachant que 380x2=760on a 106 entrées de trop.
On retrouve 16 questionnaires completement vides, qu’on peut exclure ce qui nous donne 850 questionnaires et 379 IDs uniques.
On est tjrs très loin de notre objectif de environ 760 questionnaires (758 maintenant qu’on a 379 IDs).
Donc, on a forcement des IDs pour lesquels on a >2 entrées. Voyons de les mettre en evidence.
data %>%
group_by(id) %>%
summarise(n=n()) %>%
arrange(desc(n)) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
))problematic_IDs = data %>% group_by(id) %>% summarise(n=n()) %>% filter(n>2) %>% nrow
Comme tu peux le voir on semble avoir 48 IDs presents au moins 3 fois.
Creusons plus loin pour voir si on identifie un groupe d’IDs problematique. Théoriquement, si les gens avaient respecté les régles pour la création de l’identifiant anonyme, l’identifiant devrait suivre le pattern suivant: 1 chiffre - 2 lettres - 2 chiffres.
Voyons pour combien de lignes ce pattern est respecté:
str_detect(data$id, "[:digit:]{1}[:alpha:]{2}[:digit:]{2}") %>%
table %>%
pander| FALSE | TRUE |
|---|---|
| 44 | 806 |
Cela nous dit que même si on regarde seulement les gens qui ont respecté la procedure pour la création des IDs, on à tjrs des lignes de trop. D’abord, regardons les entrés des gens qui ont tapé un ID au pif.
data %>%
filter(!str_detect(id, "[:digit:]{1}[:alpha:]{2}[:digit:]{2}")) %>%
arrange(id) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
))
Néanmoins, s’ils ont utilisé 2 fois le même identifiant aberrant on pourra quand même les apparier. Voyons donc les IDs qui apparaissent >2 fois. Les IDs aberrants qui apparaissent 2 fois on peut les utiliser, les IDs qui apparaissent qu’une seule fois, idem, on peut les garder pour la partie descriptive (carrement ils ne sera pas possible de les utiliser pour la comparaison avant-après).
data %>%
filter(!str_detect(id, "[:digit:]{1}[:alpha:]{2}[:digit:]{2}")) %>%
group_by(id) %>%
summarise(n=n()) %>%
arrange(desc(n)) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
))On voit que le seul ID problematique parmi les aberrants est “12345”.
Est-ce que la filière pourrait nous aider? Voyons.
data %>%
filter(id=="12345") %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
))On semble avoir 1 IDE et 1 MG, qui ont un doublon de “2ème test”.
Si on regarde avec attention la ligne 3 et 4 sont identiques, avec une “filière” différente.
Si la ligne 3 et 4 étaient par exemple “IDE” et la ligne 5 et 6 étaient “medecine generale” on aurait identifié des doublons –> problème résolu.
Mais dans ce cas on a des doublons ET 2 personnes qui ont rentré 2 fois le “2ème test” en changeant aussi de filière. Ou Il est donc impossible de rattacher ces questionnaires à un “test 1”.
Ce qui est très très bizarre, est que les lignes 3 et 5 sont IDENTIQUES aux lignes 4 et 6. Ce qu’on peut faire, est prendre la moyenne des lignes 3-5 et 4-6. Le même approche nous sera utile pour gérer les IDs non aberrants doublons qui sont les suivants:
data %>%
filter(str_detect(id, "[:digit:]{1}[:alpha:]{2}[:digit:]{2}")) %>%
group_by(id) %>%
mutate(n=n()) %>%
filter(n>2) %>%
relocate(n, .after = id) %>%
arrange(desc(n)) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
))data = data%>%
group_by(id, test, filiere) %>%
summarise(across(s1q1:last_col(), ~mean(., na.rm = TRUE)), .groups = "drop")Vu que “ID” ne suffit pour identifier uniquement les lignes, si on prend “ID” + “test” + “filiere” et on fait la moyenne des lignes ambigues on devrait s’en sortir pas mal.
Cela nous laisse avec 776 questionnaires.
Bon, on sait dejà qu’on a des IDs qui apparaissent trop souvent mais si on prend ID-filiere, est-ce que maintenant on a max 2 lignes par couple ID-filiere?
data %>%
group_by(id,filiere) %>%
summarise(n=n()) %>%
arrange(desc(n)) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
))## `summarise()` has grouped output by 'id'. You can override using the `.groups`
## argument.
Oui! Nous avons maintenant 413 identité uniques données par ID+FILIERE. Vu qu’on a besoin de “filière” pour identifier les gens, on va esclure les lignes où “filière” n’est pas renseigné.
filiere_na = data %>% filter(is.na(filiere)) %>% nrow()
# REMOVING MISSING "filiere" rows
data = data %>% filter(!is.na(filiere))
# How many questionnaires left?
questionnaires = nrow(data)
# How many unique identifties?
ids = data %>% distinct(id,filiere) %>% nrowOn exclu donc 9 lignes car valeur manquante. On est maintenant à 767 questionnaires pour 413 identités. Voyons combien de gens on peut apparier.
data %>%
group_by(id,filiere) %>%
summarise(n=n(), .groups="drop") %>%
arrange(desc(n)) %$%
table(n) %>%
pander(caption = "Nb questionnaires appariés et non")| 1 | 2 |
|---|---|
| 59 | 354 |
Figure 1.1: Flowchart inclusion questionnaires
data %>% distinct(id,filiere) %$%
table(filiere) %>%
as.data.frame() %>%
mutate("%" = round(Freq/sum(Freq)*100, 2)) %>%
adorn_totals() %>%
pander(caption = "**Repartition sujets par filière**")| filiere | Freq | % |
|---|---|---|
| IDE | 166 | 40.19 |
| maieutique | 25 | 6.05 |
| medecine generale | 97 | 23.49 |
| MKE | 48 | 11.62 |
| orthophonie | 28 | 6.78 |
| pharmacie | 49 | 11.86 |
| Total | 413 | 99.99 |
Voyons sur tout l’ensemble de tests, combien de données manquantes on a. Ce sont des “vraies” données manquantes OU des “NSP” qui n’était pas pertinents pour la question et vraisemblablement pris pour des “Ne sait pas”.
table(data$na_counts) %>%
as.data.frame() %>%
mutate("%" = round(Freq/sum(Freq)*100, 2)) %>%
rename("Nb missing values" = Var1) %>%
pander(caption = "**Nb de valeurs manquantes par questionnaire**")| Nb missing values | Freq | % |
|---|---|---|
| 0 | 688 | 89.7 |
| 0.333333333333333 | 1 | 0.13 |
| 0.5 | 7 | 0.91 |
| 1 | 45 | 5.87 |
| 2 | 16 | 2.09 |
| 3 | 3 | 0.39 |
| 4 | 1 | 0.13 |
| 5 | 5 | 0.65 |
| 6 | 1 | 0.13 |
On voit que les tests sont bien complets globalement sauf 16 questionnaires qui sont complétement vides. Ils sont des doublons dus à des enregistrements vides.
On peut voir que la repartition des valeurs manquantes par filière est en ligne avec la repartition des sujets par filière. Donc il n’y a pas une filière qui a laissé le questionnaire “plus vide” que les autres.
data %>%
group_by(filiere) %>%
summarise(Freq = sum(na_counts)) %>%
mutate("%" = round(Freq / sum(Freq) * 100, 2)) %>%
adorn_totals() %>%
pander(caption = "**Valeurs manquantes par filière**", align = "center")| filiere | Freq | % |
|---|---|---|
| IDE | 59.83 | 47.93 |
| maieutique | 5 | 4.01 |
| medecine generale | 30 | 24.03 |
| MKE | 9 | 7.21 |
| orthophonie | 5 | 4.01 |
| pharmacie | 16 | 12.82 |
| Total | 124.8 | 100 |
!!!!!! Quelque chose ne va pas, on a 379 sujets et 800+ questionnaires (on devrait en avoir au max 379*2). Dans le tableau suivant on voit qu’on a 199 sujets avec au moins 3 entrées (et j’ai déjà exclu les 16 dont je parlais avant).
x <- data %>%
group_by(id, filiere) %>%
summarise(n = n()) %>%
arrange(desc(n)) %>%
filter(n > 2) %>%
pull(id)
data %>%
filter(id %in% x) %>%
arrange(id, filiere, test) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
)
) Les entrées supplementaires ne sont pas des “doublons”, les valeurs ne sont pas les mêmes. Je vais donc prendre la moyenne de ces valeurs. Ci dessous le résultat, on est à 766 lignes alor que notre max theorique est 758. Il y a forcement des gens qui se sont enregistrés avec plus qu’un ID (j’ai renommé la variable “A1”).
Ca ne m’inquiète pas pour les tests appariés, je ne pense pas que des gens ont 2*2 tests appariés mais je controllerai.
data %>%
group_by(id,test,filiere) %>%
summarise(across(s1q1:s7q2,mean,na.rm=T)) %>%
mutate(na_counts = rowSums(across(s1q1:s7q2, is.na))) %>%
datatable(options = list(pageLength = 10,
#dom = 't',
scrollX = TRUE,
width = "auto",
scrollX = T
)
)## `summarise()` has grouped output by 'id', 'test'. You can override using the
## `.groups` argument.
# DF with id-filiere-pre-post pour les tests stat
test_data <- data %>%
group_by(id, filiere, test) %>%
summarise(mean_score = mean(c_across(s1q1:s7q2), na.rm = T)) %>%
pivot_wider(names_from = "test", values_from = "mean_score") %>%
filter(!is.na(post),!is.na(pre)) %>% distinct## `summarise()` has grouped output by 'id', 'filiere'. You can override using the
## `.groups` argument.
# paired cases by filiere
test_data %$%
table(filiere) %>%
as.data.frame() %>%
mutate("%" = round(Freq/sum(Freq)*100, 2)) %>%
adorn_totals() %>%
pander(caption = "Cas appariés par filière")| filiere | Freq | % |
|---|---|---|
| IDE | 135 | 38.14 |
| maieutique | 24 | 6.78 |
| medecine generale | 88 | 24.86 |
| MKE | 38 | 10.73 |
| orthophonie | 25 | 7.06 |
| pharmacie | 44 | 12.43 |
| Total | 354 | 100 |
Pour ces pourcentages les denominateur est “total de tests appariés” et non le “nombre total de tests. Donc, les IDE, representent 38.14% des tests appariés disponibles.
Mais, il y a carrement plus d’IDE que les autres filières! Oui, bien sur. Voyons
# PRE vs POST, toutes filieres confondues
t.test(test_data$post,test_data$pre, paired=T)##
## Paired t-test
##
## data: test_data$post and test_data$pre
## t = 15.96, df = 353, p-value < 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## 0.5796475 0.7425832
## sample estimates:
## mean of the differences
## 0.6611153
# PRE vs POST, filiere par filiere
t.test(test_data$post[test_data$filiere=="IDE"],test_data$pre[test_data$filiere=="IDE"], paired=T)##
## Paired t-test
##
## data: test_data$post[test_data$filiere == "IDE"] and test_data$pre[test_data$filiere == "IDE"]
## t = 9.0251, df = 134, p-value = 1.68e-15
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## 0.4998955 0.7804887
## sample estimates:
## mean of the differences
## 0.6401921